package org.richin.file.util;
import java.io.File;
import java.io.FileFilter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.commons.collections.SetUtils;

/**
 * Iterates over the files in a directory with the option of recursing through
 * subdirectories.  Directory or file names supplied in the ignore list are
 * applied to the top-level directory only.  The <code>remove</code> method is
 * not supported because the list of files is modified dynamically to optimize
 * memory usage.
 * @author Connor Garvey
 * @created December 8, 2006, 11:45 AM
 * @version 0.0.1
 * @since 0.0.1
 */
// TODO: Modify this to read files in real-time instead of caching a list
public class FilteredFileIterator implements Iterator<File> {
  private static final HashSet<File> EMPTY_SET = new HashSet<File>(0);

  private File directory;
  private boolean recurse;
  private List<File> files;
  private FileFilter filter;
  private FilteredFileIterator subdirectoryIterator;

  /**
   * Creates a new instance of FilteredFileIterator
   * @param directory The directory from which to read files
   * @param recurse True if subdirectories should be read, false otherwise
   * @param filter A filter to restrict results
   */
  @SuppressWarnings("unchecked")
  public FilteredFileIterator(File directory, boolean recurse, FileFilter
      filter) {
    this(directory, recurse, filter, SetUtils.EMPTY_SET);
  }

  /**
   * Creates a new instance of FilteredFileIterator
   * @param directory The directory from which to read files
   * @param recurse True if subdirectories should be read, false otherwise
   * @param ignore The files and directories to ignore
   * @param filter A filter to restrict results
   */
  public FilteredFileIterator(File directory, boolean recurse, FileFilter
      filter, Set<File> ignore) {
    // Validate parameters
    if ((directory == null) || !directory.exists() || !directory.isDirectory())
        {
      throw new IllegalArgumentException("Can't index images in '" + directory +
          "' because it doesn't exist or it is not a directory");
    }
    // Create the list of files and save it
    String path = directory.getAbsolutePath();
    if (!path.endsWith(File.separator)) {
      path += File.separator;
    }
    String[] fileNames = directory.list();
    files = new LinkedList<File>();
    File file = null;
    for (int i = 0; i < fileNames.length; i++) {
      file = new File(path + fileNames[i]);
      // Only add if we aren't ignoring it
      if (!ignore.contains(file)) {
        // If it's a directory and we're recursing, add it
        if (file.isDirectory()) {
          if (recurse) {
            files.add(file);
          }
        }
        // If it's not a directory, add it if it's an image
        else if (filter.accept(file)) {
          files.add(file);
        }
      }
    }
    // Save variables
    this.directory = directory;
    this.recurse = recurse;
    this.filter = filter;
  }

  public boolean hasNext() {
    if (subdirectoryIterator != null) {
      if (subdirectoryIterator.hasNext()) {
        return true;
      }
      else {
        subdirectoryIterator = null;
      }
    }
    if (this.files.isEmpty()) {
      return false;
    }
    File file = this.files.get(0);
    if (file.isDirectory()) {
      if (!recurse) {
        throw new IllegalStateException("Not recursing, so can't iterate " +
            "over directories");
      }
      while (true) {
        String directoryPath = new String(directory.getAbsolutePath());
        if (!directoryPath.endsWith(File.separator)) {
          directoryPath += File.separator;
        }
        File subdirectory = new File(directoryPath + file.getName());
        subdirectoryIterator = new FilteredFileIterator(subdirectory, recurse,
            filter, EMPTY_SET);
        // Since this is the new iterator, remove it from the list
        this.files.remove(0);
        if (subdirectoryIterator.hasNext()) {
          return true;
        }
        else {
          if (this.files.isEmpty()) {
            return false;
          }
          file = this.files.get(0);
          if (file.isFile()) {
            return true;
          }
        }
      }
    }
    else {
      return true;
    }
  }

  public File next() {
    if (subdirectoryIterator != null) {
      if (subdirectoryIterator.hasNext()) {
        return subdirectoryIterator.next();
      }
      else {
        subdirectoryIterator = null;
      }
    }
    if (this.files.isEmpty()) {
      throw new NoSuchElementException();
    }
    File file = this.files.remove(0);
    if (file.isDirectory()) {
      if (!recurse) {
        throw new IllegalStateException("Not recursing, so can't iterate " +
            "over directories");
      }
      while (true) {
        String directoryPath = new String(directory.getAbsolutePath());
        if (!directoryPath.endsWith(File.separator)) {
          directoryPath += File.separator;
        }
        File subdirectory = new File(directoryPath + file.getName());
        subdirectoryIterator = new FilteredFileIterator(subdirectory, recurse,
            filter, EMPTY_SET);
        if (subdirectoryIterator.hasNext()) {
          return subdirectoryIterator.next();
        }
        else {
          file = this.files.remove(0);
          if (file.isFile()) {
            return file;
          }
        }
      }
    }
    else {
      return file;
    }
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }
  public static void main(String[] args) {
	FilteredFileIterator fileIterator=new FilteredFileIterator(new File("C:\\Documents and Settings\\Administrator\\桌面\\stock\\fileutil_src"),true,new FileFilter(){

		public boolean accept(File pathname) {
		
			return "java".equals(FileUtils.getExtension(pathname));
		}
		
	});
	while (fileIterator.hasNext()) {
		File file = (File) fileIterator.next();
		System.out.println(file.getPath());
	}
}
}

